home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Almathera Ten Pack 3: CDPD 3
/
Almathera Ten on Ten - Disc 3: CDPD3.iso
/
scope
/
051-075
/
scopedisk69
/
cclat
/
src
/
cc.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-03-19
|
24KB
|
881 lines
/*
* cc -- unix-like compiler driver for amiga lattice c
*
* adapted for lattice version 5.0 and extensively modified
* by Miles Bader, from the original by Fred Fish (copyright
* follows).
*/
/************************************************************************
* *
* Copyright (c) 1985, Fred Fish *
* All Rights Reserved *
* *
* This software and/or documentation is released into the *
* public domain for personal, non-commercial use only. *
* Limited rights to use, modify, and redistribute are hereby *
* granted for non-commercial purposes, provided that all *
* copyright notices remain intact and all changes are clearly *
* documented. The author makes no warranty of any kind with *
* respect to this product and explicitly disclaims any implied *
* warranties of merchantability or fitness for any particular *
* purpose. *
* *
************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include <proto/dos.h>
#include <ios1.h> /* magic */
#include "common.h"
#include "list.h"
#include "dstr.h"
#include "op.h"
#include "options.h"
#include "config.h"
static char *locate(char *file,struct list *list); /* find a file */
static void cleanObjects(int mustExist); /* Remove .o for link and go mode */
static int makeObjs(); /* Reduce to list of object files */
static int parseCommandLine(); /* Deal with command line */
static int compile(struct op *op); /* Translate from .c to .o */
static int assemble(struct op *op); /* Translate from .s to .o */
static int link(); /* Gather .o's into executable */
static int runCmd(struct dstr *cmd);
#ifdef amiga
#define CHECK_ABORT chkabort()
#else
#define CHECK_ABORT /* NULL expansion */
#endif /* amiga */
DBUG_GLOBALS
/*
* flags set by command line arguments
*/
static int debugLevel=DEFAULT_DEBUGLEVEL; /* -g (debugging level) */
struct list *libDirs=NULL;
struct list *incDirs=NULL;
struct list *binDirs=NULL;
struct list *defines=NULL;
struct list *undefines=NULL;
struct list *libs=NULL;
struct list *pass1PassThrus=NULL;
struct list *pass2PassThrus=NULL;
struct list *optPassThrus=NULL;
struct list *linkPassThrus=NULL;
struct list *ops=NULL;
static char *outfile=DEFAULT_EXEC; /* output file name from linker */
static char *tempDir=TEMPDIR; /* where to keep quad files */
static char *sinkFile=SINKFILE; /* where to put output from passes */
struct dstr *cmdLine=NULL;
static int abortmsg()
{
if(options_IsSet(OPT_ECHO))
fprintf(stderr,"cc: aborting\n");
return 1; /* abort */
}
static int cleanup(status)
int status;
{
if(cmdLine!=NULL) dstr_Free(cmdLine);
if(libDirs!=NULL) list_Free(libDirs);
if(incDirs!=NULL) list_Free(incDirs);
if(binDirs!=NULL) list_Free(binDirs);
if(defines!=NULL) list_Free(defines);
if(undefines!=NULL) list_Free(undefines);
if(libs!=NULL) list_Free(libs);
if(pass1PassThrus!=NULL) list_Free(pass1PassThrus);
if(pass2PassThrus!=NULL) list_Free(pass2PassThrus);
if(optPassThrus!=NULL) list_Free(optPassThrus);
if(linkPassThrus!=NULL) list_Free(linkPassThrus);
if(ops!=NULL) list_Free(ops);
return status;
}
static void init()
{
int *opt;
options_Init();
for(opt=defaultOptions; *opt!=0; opt++)
options_Set(*opt);
cmdLine=dstr_Create(30); /* global command buffer */
libDirs=list_Create((void **)libDirsInit);
incDirs=list_Create((void **)incDirsInit);
binDirs=list_Create((void **)binDirsInit);
defines=list_Create((void **)definesInit);
undefines=list_Create((void **)undefinesInit);
libs=list_Create((void **)libsInit);
pass1PassThrus=list_Create(NULL);
pass2PassThrus=list_Create(NULL);
optPassThrus=list_Create(NULL);
linkPassThrus=list_Create(NULL);
ops=list_Create(NULL);
list_SetFree(ops,op_Free);
}
void main(argc, argv)
int argc;
char *argv[];
{
int status;
DBUG_ENTER("main");
onexit(cleanup);
init();
onbreak(abortmsg);
if(parseCommandLine(argc,argv)==0)
fatal("no files specified");
cleanObjects(FALSE); /* get rid of existing object files if any */
status=makeObjs();
if(status==0 && options_IsSet(OPT_LINK)){
status=link();
if(list_GetLen(ops)==1)
cleanObjects(TRUE);
}
exit(status);
}
/*
* The following macro is used to allow optional whitespace between
* an option and it's argument. Argp is left pointing at the option
* and argv and argc are adjusted accordingly if necessary.
*
* Note that there is no check for missing option arguments. In
* particular, -o -V will blindly take -V as the output file name.
*
*/
#define XARG(argc,argv,argp) {if(*++argp=='\0'){argp=(*argv++);argc--;}}
static int parseCommandLine(argc,argv)
int argc;
char **argv;
{
register char *argp;
DBUG_ENTER("parseCommandLine");
argc--;
argv++;
while(argc-- > 0){
CHECK_ABORT;
argp=*argv++;
if(*argp=='+')
options_SetByName(argp+1);
else if(*argp!='-')
list_Add(ops,(void *)op_Create(argp));
else
switch(*++argp){
case 'c':
options_Clear(OPT_LINK); /* don't link object modules */
break;
case 'p': /* pass thru args to a pass */
switch(*++argp){
case '1': /* pass 1 */
XARG(argc,argv,argp);
list_Add(pass1PassThrus,argp);
break;
case '2': /* pass 2 */
XARG(argc,argv,argp);
list_Add(pass2PassThrus,argp);
break;
case 'o': /* optimizer */
XARG(argc,argv,argp);
list_Add(optPassThrus,argp);
break;
case 'l': /* linking */
XARG(argc,argv,argp);
list_Add(linkPassThrus,argp);
break;
default:
fatal("unknown pass '%c'; must be one of [12ol]",argp[-1]);
}
break;
case 'D':
XARG(argc,argv,argp);
list_Add(defines,(void *)argp);
break;
case 'E':
warning("-E unimplemented, converted to -P instead");
/* fall through */
case 'P':
options_Clear(OPT_COMPILE);
options_Clear(OPT_ASSEMBLE);
options_Clear(OPT_LINK);
break;
case 'g':
if(*++argp!='\0') /* optional debuggin level */
debugLevel=atoi(argp);
options_Set(OPT_DEBUG);
break;
case 'I':
XARG(argc,argv,argp);
list_Add(incDirs,(void *)argp);
break;
case 'l':
XARG(argc,argv,argp);
list_Add(libs,(void *)argp);
break;
case 'L':
XARG(argc,argv,argp);
list_Add(libDirs,(void *)argp);
break;
case 'B':
XARG(argc,argv,argp);
list_Add(binDirs,(void *)argp);
break;
case 'O':
options_Set(OPT_OPTIMIZE);
break;
case 'o':
XARG(argc,argv,argp);
outfile=argp;
break;
case 't': /* warning, non-standard */
XARG(argc,argv,argp);
tempDir=argp;
break;
case 'S':
warning("-S option not yet implemented, ignored");
options_Clear(OPT_ASSEMBLE);
break;
case 'U':
XARG(argc,argv,argp);
list_Add(undefines,(void *)argp);
break;
default:
fatal("unknown option '%c'",*argp);
break;
}
}
DBUG_RETURN(int,list_GetLen(ops));
}
/*
* For each operand, do compilation or assembly as necessary, to
* reduce to an object file in the current directory.
*/
static int makeObjs()
{
int n;
void **els;
int numOps=list_GetLen(ops);
int status=0;
DBUG_ENTER("makeObjs");
for(n=numOps,els=list_GetEls(ops); n>0; n--,els++){
struct op *op=(struct op *)*els;
CHECK_ABORT;
if(numOps>1 && (op_IsC(op) || op_IsS(op)))
printf("%s.%s:\n",op->rootname,op->suffix);
if(op_IsC(op))
status=compile(op);
else if(op_IsS(op))
status=assemble(op);
}
DBUG_RETURN(int,status);
}
/* handy little routine */
void addfList(ds,fmt,list)
struct dstr *ds;
char *fmt;
struct list *list;
{
int n;
void **els;
for(n=list_GetLen(list),els=list_GetEls(list); n>0; n--,els++)
dstr_Addf(ds,fmt,(char *)*els);
}
/*
* Note that commands to cc of the form "-l<name>" get interpreted
* to mean use a library called "name.lib" from the library
* directory.
* -lm is specially interpreted to be the appropriate math library.
*/
static int link()
{
int n;
void **els;
DBUG_ENTER("link");
dstr_Clear(cmdLine);
dstr_Addf(cmdLine,"%s ",locate("blink",binDirs));
if(options_IsSet(OPT_DETACH))
dstr_Append(cmdLine,locate("cback.o",libDirs));
else if(options_IsSet(OPT_RESIDENT)){
if(options_IsSet(OPT_CATCH))
dstr_Append(cmdLine,locate("catchres.o",libDirs));
else
dstr_Append(cmdLine,locate("cres.o",libDirs));
}else if(options_IsSet(OPT_CATCH))
dstr_Append(cmdLine,locate("catch.o",libDirs));
else
dstr_Append(cmdLine,locate("c.o",libDirs));
for(n=list_GetLen(ops),els=list_GetEls(ops); n>0; n--,els++){
struct op *op=(struct op *)*els;
char *name=(op_IsO(op) ? op->rootname : op->basename);
dstr_Addf(cmdLine,"+%s.o",name);
}
if(options_IsSet(OPT_TINYMAIN))
dstr_Addf(cmdLine," define __main=__tinymain ");
dstr_Addf(cmdLine," library ");
for(n=list_GetLen(libs),els=list_GetEls(libs); n>0; n--,els++){
char buffer[50];
char *lib=(char *)*els;
if(strcmp(lib,"m")==0){
/* map -lm to appropiate library */
strcpy(buffer,"lib:lcm");
if(options_IsSet(OPT_FFP))
strcat(buffer,"ffp");
else if(options_IsSet(OPT_IEEE))
strcat(buffer,"ieee");
else if(options_IsSet(OPT_881))
strcat(buffer,"881");
/*
* there are only r and s libraries for lcm.lib in the lattice
* distribution; if there is a way to get the lcmieee, lcmffp,
* & lcm881 libraries to work with reg-args & short-ints, I
* couldn't find it.
*/
if(options_IsSet(OPT_SHORTINTS))
strcat(buffer,"s");
if(options_IsSet(OPT_REGARGS))
strcat(buffer,"r");
strcat(buffer,".lib");
}else if(strcmp(lib,"lc")==0){
/* do the standard c library */
strcpy(buffer,"lib:lc");
if(options_IsSet(OPT_SHORTINTS))
strcat(buffer,"s");
if(options_IsSet(OPT_REGARGS))
strcat(buffer,"r");
if(options_IsSet(OPT_ABSDATA))
strcat(buffer,"nb");
strcat(buffer,".lib");
if(options_IsSet(OPT_REGARGS)){
/*
* Now, do it AGAIN, but without registers, to catch any missing refs.
* I don't think this is exactly the right thing to do, but there seem
* to be internal functions missing from the register-fied libraries
*/
strcat(buffer,"+lib:lc");
if(options_IsSet(OPT_SHORTINTS))
strcat(buffer,"s");
if(options_IsSet(OPT_ABSDATA))
strcat(buffer,"nb");
strcat(buffer,".lib");
}
}else{
sprintf(buffer,"%s.lib",lib);
strcpy(buffer,locate(buffer,libDirs));
}
if(n==1)
dstr_Append(cmdLine,buffer);
else
dstr_Addf(cmdLine,"%s+",buffer);
}
if(outfile==NULL)
/* there better be at least one source file! */
outfile=((struct op *)list_GetEls(ops))->basename;
dstr_Addf(cmdLine," to %s map nil:",outfile);
addfList(cmdLine," %s",linkPassThrus);
DBUG_RETURN(int,runCmd(cmdLine));
}
/*
* compile one operand from a C source program to an object module.
*/
static int compile(op)
struct op *op;
{
int status=0;
DBUG_ENTER("compile");
if(options_IsSet(OPT_ASSEMBLE)){
status=pass1(op);
if(status==0 && options_IsSet(OPT_COMPILE)){
CHECK_ABORT;
if(options_IsSet(OPT_OPTIMIZE))
status=optimize(op);
if(status==0)
status=pass2(op);
}
}
DBUG_RETURN(int,status);
}
/*
* Note that because of brain-damage in the fact that -p to lc1 removes
* all predefined defs, we must add them so replacing -c with -P in the
* cc command line will result in the same set of predefined symbols.
* This is rather ugly and leaves a hole for future problems if we
* get out of sync with respect to what names the compiler predefines.
*/
static int pass1(op)
register struct op *op;
{
char tmpFile[MAXPATH];
int status;
int n;
void **els;
DBUG_ENTER("pass1");
dstr_Clear(cmdLine);
dstr_Append(cmdLine,locate((options_IsSet(OPT_BIGLC1) ? "lc1b" : "lc1"),binDirs));
if(options_IsSet(OPT_DEBUG)){
dstr_Addf(cmdLine," -d%d",debugLevel);
if(options_IsSet(OPT_OPTIMIZE)){
warning("-g incompatible with -O, -O turned off");
options_Clear(OPT_OPTIMIZE);
}
}
if(options_IsSet(OPT_FFP))
dstr_Addf(cmdLine," -ff");
else if(options_IsSet(OPT_881))
dstr_Addf(cmdLine," -f8");
else if(options_IsSet(OPT_IEEE))
dstr_Addf(cmdLine," -fi");
else
dstr_Addf(cmdLine," -fl");
if(options_IsSet(OPT_SHORTINTS))
dstr_Addf(cmdLine," -w");
if(options_IsSet(OPT_LONGALIGN))
dstr_Addf(cmdLine," -l");
if(options_IsSet(OPT_020))
dstr_Addf(cmdLine," -m2");
else if(options_IsSet(OPT_030))
dstr_Addf(cmdLine," -m3");
if(options_IsSet(OPT_CATCH) && !options_IsSet(OPT_ABSDATA)){
warning("+catch implies +abs-data");
options_Set(OPT_ABSDATA);
}
dstr_Addf(cmdLine," -b%d",options_IsSet(OPT_ABSDATA) ? 0 : 1);
dstr_Addf(cmdLine," -r%d",options_IsSet(OPT_ABSCODE) ? 0 : 1);
if(options_IsSet(OPT_REGARGS))
dstr_Addf(cmdLine,"r"); /* part of -r switch */
{
char buf[15], *p=buf;
if(options_IsSet(OPT_ANSI)) *p++='a';
if(options_IsSet(OPT_CPP)) *p++='+';
if(options_IsSet(OPT_TRAD)) *p++='l', *p++='o';
if(options_IsSet(OPT_REGARGS)) *p++='r';
if(options_IsSet(OPT_PURESTRINGS)) *p++='s';
if(p>buf){
*p='\0';
dstr_Addf(cmdLine," -c%s",buf);
}
}
if(options_IsSet(OPT_COMPILE)){
strcpy(tmpFile,tempDir);
strcat(tmpFile,op->basename);
strcat(tmpFile,".q");
dstr_Addf(cmdLine," -o%s",tmpFile);
}else{
strcpy(tmpFile,op->basename);
strcat(tmpFile,".pp");
dstr_Addf(cmdLine," -o%s -p",tmpFile);
}
for(n=list_GetLen(undefines),els=list_GetEls(undefines); n>0; n--,els++){
/*************************
dstr_Addf(cmdLine," -u%s",(char *)*els);
**************************/
warning("-U%s ignored! (unimplemented)",(char *)*els);
}
addfList(cmdLine," -d%s",defines);
addfList(cmdLine," %s",pass1PassThrus);
for(n=list_GetLen(incDirs),els=list_GetEls(incDirs); n>0; n--,els++){
char *id=(*els);
int len=strlen(id);
dstr_Addf(cmdLine," -i%s",*els);
if(len>0 && id[len-1]!=':' && id[len-1]!='/')
dstr_Addf(cmdLine,"/");
}
dstr_Addf(cmdLine," %s",op->rootname);
status=runCmd(cmdLine);
if(status==0 && options_IsSet(OPT_EXEC) && !readable(tmpFile))
status=1;
DBUG_RETURN(int,status);
}
/* run the optimizer */
static int optimize(op)
struct op *op;
{
DBUG_ENTER("optimize");
dstr_Clear(cmdLine);
dstr_Addf(cmdLine,"%s %s%s",locate("go",binDirs),tempDir,op->basename);
addfList(cmdLine," %s",optPassThrus);
DBUG_RETURN(int,runCmd(cmdLine));
}
/*
* Run second pass of compiler on a single operand.
*/
static int pass2(op)
struct op *op;
{
char objFile[MAXPATH];
int status;
#ifdef DBUG
fprintf(stderr,"Entering pass2...\n");
#endif
DBUG_ENTER("pass2");
dstr_Clear(cmdLine);
dstr_Append(cmdLine,locate("lc2",binDirs));
if(!options_IsSet(OPT_STACKCHECK))
dstr_Addf(cmdLine," -v");
strcpy(objFile,op->basename);
strcat(objFile,".o");
dstr_Addf(cmdLine," -o%s",objFile);
dstr_Addf(cmdLine," %s%s",tempDir,op->basename);
addfList(cmdLine," %s",pass2PassThrus);
status=runCmd(cmdLine);
if(status==0 && options_IsSet(OPT_EXEC) && !readable(objFile))
status=1;
DBUG_RETURN(int,status);
}
/*
* I have not yet had occasion to use the macro assembler,so this
* part is not yet implemented. If anyone wants to send me the
* appropriate code, I will be glad to install it.
*/
static int assemble(op)
struct op *op;
{
DBUG_ENTER("assemble");
warning("assembly pass not yet implemented");
DBUG_RETURN(int,1);
}
static void filteredOutputFrom(source,filter)
FILE *source;
char **filter;
{
char buf[200];
DBUG_ENTER("FilteredOutputFrom");
while(!feof(source)){
char **ignore;
fgets(buf,sizeof(buf),source);
for(ignore=filter; *ignore!=NULL; ignore++)
if(strncmp(*ignore,buf,strlen(*ignore))==0)
break;
if(*ignore==NULL)
fputs(buf,stdout);
}
DBUG_VOID_RETURN;
}
#ifdef AMIGA
static int execToFile(cmd,sink)
char *cmd;
FILE *sink;
{
/* this is a dopey version that uses Execute, since I can't get fork to work */
long fh;
DBUG_ENTER("execToFile");
if(sink==NULL)
fh=0;
else
fh=chkufb(fileno(sink))->ufbfh;
DBUG_RETURN(int,Execute(cmd,0L,fh)!=(-1));
#if 0
char *argv[50],**argp=argv;
struct ProcID child;
static struct FORKENV env={0,0,0,0,0,NULL};
int i;
DBUG_ENTER("execToFile");
if(cmd==NULL)
DBUG_RETURN(int,1);
/* this is sort of stupid, cause forkv will just put them back together,
* but oh well...
*/
*argp=strtok(cmd," \t");
while(*argp!=NULL)
*++argp=strtok(NULL," \t");
if(sink==NULL)
env.std_out=0;
else{
struct UFB *ufb=chkufb(fileno(sink));
int i;
printf("argv[%d]:",argp-argv);
for(argp=argv;*argp!=NULL;argp++)
printf(" %s",*argp);
putchar('\n');
printf("env: %d, %d, %d, %d, %d, %d\n",
env.priority, env.stack, env.std_in, env.std_out,
env.console, env.msgport);
printf("ufb==0x%x, fh==0x%x, go? ",ufb,ufb->ufbfh);
fflush(stdout);
env.std_out=ufb->ufbfh; /* magic to get amigados handle from FILE */
}
if((i=forkv(argv[0],argv,&env,&child))==-1){
poserr("forkv");
DBUG_RETURN(int,1);
}
printf("Fork returns: %d\n",i);
DBUG_RETURN(int,wait(&child));
#endif
}
#endif
static int runCmd(cmd)
struct dstr *cmd;
{
int status;
char *buf=dstr_GetBuf(cmd);
DBUG_ENTER("RunCommand");
DBUG_3("cmd","execute '%s'",buf);
if(options_IsSet(OPT_ECHO)){
puts(buf);
fflush(stdout);
}
CHECK_ABORT;
if(options_IsSet(OPT_EXEC)){
FILE *sink;
if(sinkFile!=NULL && options_IsSet(OPT_FILTER)){
sink=fopen(sinkFile,"w+");
if(sink==NULL)
perror(sinkFile);
}else
sink=NULL;
status=execToFile(buf,sink);
DBUG_3("sys","subcommand returns status %d",status);
if(sink!=NULL){
rewind(sink);
filteredOutputFrom(sink,filteredPrefixes);
fclose(sink);
unlink(sinkFile);
}
DBUG_RETURN(int,status);
}else
DBUG_RETURN(int,0);
}
/*
* Look through the list of paths pointed to by "vec" until we find
* a file with name given pointed to by "namep". If none is found,
* the name pointed to by namep is returned.
*/
static char *locate(name,paths)
char *name;
struct list *paths;
{
static char namebuf[MAXPATH];
void **els;
int n;
DBUG_ENTER("locate");
if(strchr(name,':')!=NULL || *name=='/')
return name; /* absolute */
for(n=list_GetLen(paths),els=list_GetEls(paths); n>0; n--,els++){
int len=strlen((char *)*els);
strcpy(namebuf,(char *)*els);
if(len>0 && namebuf[len-1]!=':' && namebuf[len-1]!='/')
strcat(namebuf,"/");
strcat(namebuf,name);
DBUG_3("try","look for '%s'",namebuf);
if(readable(namebuf)){
name=namebuf;
break;
}
}
DBUG_RETURN(char *,name);
}
/*
* Check to see if the file exists and is readable.
*/
static int readable(name)
char *name;
{
register int status=0;
register long fildes;
DBUG_ENTER("readable");
#ifdef unix
fildes=open(name,O_RDONLY);
if(fildes >= 0){
(void) close(fid4s);
status=1;
}
#else
fildes=Lock(name,ACCESS_READ);
if(fildes != 0){
UnLock(fildes);
status=1;
}
#endif
DBUG_RETURN(int,status);
}
/*
* If an executable is made from a single C file, the normal behavior
* for the unix environment is to treat the .o file as an intermediate
* file and remove it, so we follow suit.
*/
static void cleanObjects(mustExist)
int mustExist;
{
int n;
void **els;
DBUG_ENTER("cleanObjects");
for(n=list_GetLen(ops),els=list_GetEls(ops); n>0; n--,els++){
char buf[MAXPATH];
struct op *op=(struct op *)*els;
if(!op_IsO(op)){
strcpy(buf,op->basename);
strcat(buf,".o");
if(unlink(buf)!=0 && mustExist)
warning("can't delete '%s'",buf);
}
}
DBUG_VOID_RETURN;
}